﻿//========================================================= 
/**@file DataTable.h
 * @brief 关系型数据库数据存储结构
 * 
 * @date 2018-06-21   14:20:00
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_DATATABLE_H_
#define _LIBZHOUYB_DATATABLE_H_
//--------------------------------------------------------- 
#include <map>
#include <vector>

#include "../lmdb/lmdb_extractor.h"
#include "../lmdb/LmdbEnumerator.h"
using namespace zhou_yb::wrapper::lmdb_extractor;

#include "../protobuf/protobuf_extractor.h"
using namespace zhou_yb::wrapper::protobuf_extractor;

#include "../../extension/logcat/LogCat.h"
using zhou_yb::extension::logcat::LogCat;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace datatable {
//--------------------------------------------------------- 
class ProtoFileHelper
{
protected:
    ProtoFileHelper() {}
public:
    /// 将文件保存为字符串数据
    static bool Encode(const string& file, string& buff)
    {
        std::ifstream fin;
        fin.open(file, ios::in);
        if(fin.fail())
            return false;
        std::getline(fin, buff, static_cast<char>(EOF));
        fin.close();
        return true;
    }
    /// 还原二进制数据为文件
    static bool Decode(const ByteArray& buff, const string& file)
    {
        std::ofstream fout;
        fout.open(file, ios::out);
        if(fout.fail())
            return false;
        fout.write(buff.GetString(), buff.GetLength());
        fout.close();
        return true;
    }
    /// 将文件夹存为表
    static size_t FolderToTable(const string& dir, lmdb_iterator itr)
    {
        list_t<string> files;
        FolderHelper::EnumFiles(dir.c_str(), files, nullptr, ".proto");

        size_t count = 0;
        for(auto file : files)
        {
            string buff;
            string fullpath = dir;
            fullpath += PATH_SEPARATOR;
            fullpath += file;

            if(Encode(fullpath, buff))
            {
                ByteArray f(file.c_str(), file.length());
                size_t index = StringConvert::LastIndexOf(f, '.');
                f = f.SubArray(0, index);
                if(itr.put(f, ByteArray(buff.c_str(), buff.length())))
                    ++count;
            }
        }
        return count;
    }
    /// 将表还原为文件夹
    static size_t TableToFolder(lmdb_iterator itr, const string& dir)
    {
        LmdbStringEnumerator enumer = (*itr);
        FolderHelper::CreateFolder(dir.c_str());
        size_t count = 0;
        while(enumer.MoveNext())
        {
            string path = dir;
            path += PATH_SEPARATOR;
            path += enumer.Current().Key;
            path += ".proto";

            string& val = enumer.Current().Value;
            ByteArray buff(val.c_str(), val.length());
            if(ProtoFileHelper::Decode(buff, path.c_str()))
            {
                ++count;
            }
        }
        return count;
    }
};
//--------------------------------------------------------- 
struct IRowBy
{
    virtual bool Find(lmdb_const_iterator& itr) = 0;
};
struct IColumnBy
{
    virtual WeakMessageField Find(MessageAny& msg) = 0;
};
class ByIndex : public IRowBy, public IColumnBy
{
protected:
    uint _index;
public:
    ByIndex(uint index) : _index(index) {}
    virtual bool Find(lmdb_cursor_const_iterator& itr)
    {
        if(!itr.first())
            return false;
        if(_index == 0)
            return true;

        uint i = 0;
        while(i++ < _index)
        {
            if(!itr.next())
                return false;
        }
        return true;
    }
    virtual WeakMessageField Find(MessageAny& msg)
    {
        auto d = msg.GetDescriptor();
        if(_index >= static_cast<uint>(d->field_count()))
            return WeakMessageField();
        MessageAny::FieldType field = d->field(_index);
        return msg[field];
    }
};
class ByKey : public IRowBy, public IColumnBy
{
protected:
    ByteBuilder _key;
public:
    ByKey(uint key)
    {
        ByteConvert::FromObject(key, _key);
    }
    ByKey(const ByteArray& key)
    {
        _key.Append(key);
    }
    inline virtual bool Find(lmdb_cursor_const_iterator& itr)
    {
        return itr.get(_key);
    }
    inline virtual WeakMessageField Find(MessageAny& msg)
    {
        return msg[_key.GetString()];
    }
};
class ByNumber : public IColumnBy
{
protected:
    int _number;
public:
    ByNumber(int number) : _number(number) {}
    inline virtual WeakMessageField Find(MessageAny& msg)
    {
        return msg[_number];
    }
};
//--------------------------------------------------------- 
/**
 * @brief 数据行,维护一个lmdb游标
 * @date 2018-06-26 16:17
 */
class DataRow
{
public:
    typedef const google::protobuf::Descriptor* DescriptorType;
protected:
    ByteBuilder _key;
    MessageAny _message;
public:
    DataRow() {}
    DataRow(const ByteArray& key, const MessageAny& msg) : _key(key), _message(msg) {}
    inline bool IsNull() const
    {
        return _message.IsNull();
    }
    inline const ByteArray& Key() const
    {
        return _key;
    }
    inline const MessageAny& Message() const
    {
        return _message;
    }
    inline WeakMessageField Column(IColumnBy& by)
    {
        return by.Find(_message);
    }
    inline WeakMessageField operator[](IColumnBy& by)
    {
        return Column(by);
    }
    inline WeakMessageField operator[](const string& key)
    {
        return _message[key];
    }
    inline size_t Count() const
    {
        return _message.GetDescriptor()->field_count();
    }
    string ToString() const
    {
        size_t count = _message.GetDescriptor()->field_count();
        string str = "{";
        for(size_t i = 0; i < count; ++i)
        {
            WeakMessageField::FieldType field = _message.GetDescriptor()->field(i);
            bool isString = field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_STRING;
            str += '\"';
            str += field->name();
            str += "\":";
            if(isString) str += '\"';
            str += _message[field].ToString();
            if(isString) str += '\"';
            str += ',';
        }
        if(count > 0) str.pop_back();
        str += "}";
        return str;
    }
};
/**
 * @brief DataRow枚举器
 * @date 2018-06-26 17:02
 */
class DataRowEnumerator : public IEnumerable<ArgValue<uint, ByteArray> >
{
public:
    typedef ArgValue<uint, ByteArray> ArgType;
protected:
    lmdb_cursor_const_iterator _itr;
    ArgType _current;
    bool _isEof;
public:
    DataRowEnumerator(const lmdb_const_dbi& dbi) : _itr(dbi.cursor()) { Reset(); }
    DataRowEnumerator(const lmdb_dbi& dbi)
    {
        lmdb_const_dbi readonly_dbi = dbi;
        _itr = readonly_dbi.cursor();
        Reset();
    }
    DataRowEnumerator(const lmdb_const_cursor& cursor) : _itr(cursor) { Reset(); }
    DataRowEnumerator(const lmdb_cursor_const_iterator& itr) : _itr(itr) { Reset(); }

    /// 迭代接口
    virtual bool MoveNext()
    {
        if(_isEof)
            return false;

        ByteConvert::ToObject(_itr.Key, _current.Key);
        _current.Value = _itr.Value;

        _isEof = !_itr.next();
        return true;
    }
    /// 重置迭代状态
    virtual void Reset()
    {
        _isEof = !_itr.first();
    }
    /// 获取当前的值
    virtual ArgType& Current()
    {
        return _current;
    }
    /// 枚举是否结束
    virtual bool IsEof() const
    {
        return _isEof;
    }
};
/**
 * @brief 数据表
 * @date 2018-06-26 16:17
 */
class DataTable : public DeviceBehavior, public IEnumerator<DataRowEnumerator>
{
public:
    typedef DataRowEnumerator EnumeratorType;
    typedef DataRowEnumerator::ArgType RowData;
protected:
    lmdb_dbi _dbi;
    std::vector<string> _columns;
    MessageAnyFacroty _factory;
public:
    virtual EnumeratorType GetEnumerator() const
    {
        return EnumeratorType(_dbi);
    }
    /**
     * @brief 从lmdb表中加载
     * @date 2018-06-26 09:32
     */
    bool Load(const lmdb_dbi& dbi, MessageLoader& loader)
    {
        LOG_FUNC_NAME();
        ASSERT_FuncErrRet(dbi.is_valid(), DeviceError::ArgIsNullErr);
        string name = dbi.get_name();
        LOGGER(_log << "Name:<" << name << ">\n");
        _factory.Set(loader, name);
        const google::protobuf::Descriptor* descriptor = loader.FindMessageType(name);
        for(int i = 0; i < descriptor->field_count(); ++i)
        {
            auto field = descriptor->field(i);
            _columns.push_back(field->name());
            LOGGER(_log << "Field:<" << field->name() << ">\n");
        }
        _dbi = dbi;
        return _logRetValue(true);
    }
    /// 返回表元素行数
    size_t Count()
    {
        MDB_stat stat;
        if(!_dbi.get_stat(stat))
            return 0;
        return stat.ms_entries;
    }
    /// 返回最大的元素Key
    uint MaxKey()
    {
        lmdb_cursor_const_iterator itr = _dbi.cursor();
        if(!itr.last())
            return 0;
        uint k = 0;
        ByteConvert::ToObject(itr.Key, k);
        return (k + 1);
    }
    /// 列名(只读)
    const std::vector<string>& Columns() const
    {
        return _columns;
    }
    /// 获取原始的数据行
    RowData RowLine(IRowBy& by)
    {
        LOG_FUNC_NAME();
        lmdb_cursor_const_iterator itr = _dbi.cursor();
        if(!by.Find(itr))
        {
            _logErr(DeviceError::ArgErr);
            return RowData();
        }
        RowData row;
        ByteConvert::ToObject(itr.Key, row.Key);
        row.Value = itr.Value;
        LOGGER(_log << "Row:[" << _hex(row.Key) << "]:<" << _hex(row.Value) << ">\n");
        return row;
    }
    /// 获取数据对象
    DataRow Row(IRowBy& by)
    {
        LOG_FUNC_NAME();
        lmdb_cursor_const_iterator itr = _dbi.cursor();
        if(!by.Find(itr))
        {
            _logErr(DeviceError::ArgErr);
            return DataRow();
        }
        LOGGER(_log << "Row:[" << _hex(itr.Key) << "]:<" << _hex(itr.Value) << ">\n");
        return DataRow(itr.Key, _factory.Create(itr.Value));
    }
    /// 将RowData转为DataRow对象
    DataRow Row(const RowData& data)
    {
        ByteBuilder key(4);
        ByteConvert::FromObject(data.Key, key);
        return DataRow(key, _factory.Create(data.Value));
    }
    /// 新增行
    DataRow New(ByteArray data, uint* pKey = nullptr, uint* pIndex = nullptr)
    {
        LOG_FUNC_NAME();
        uint key = MaxKey();
        uint index = Count();
        LOGGER(_log << "Key:<" << key << ">,Index:<" << index << ">\n");
        ByteBuilder k(4);
        ByteConvert::FromObject(key, k);

        lmdb_cursor_iterator itr = _dbi.cursor();
        if(!itr.is_valid() || !itr.put(k, data))
        {
            _logErr(DeviceError::DevOpenErr, itr.error_msg().c_str());
            return DataRow();
        }
        if(pKey != nullptr)
            (*pKey) = key;
        if(pIndex != nullptr)
            (*pIndex) = index;
        return DataRow(k, _factory.Create(data));
    }
    /// 删除行
    bool Delete(IRowBy& by)
    {
        LOG_FUNC_NAME();
        lmdb_cursor_const_iterator ritr = _dbi.cursor();
        if(!by.Find(ritr))
        {
            _logErr(DeviceError::ArgErr);
            return _logRetValue(false);
        }
        lmdb_cursor_iterator itr = _dbi.cursor();
        ASSERT_FuncErrInfoRet(itr.erase(ritr.Key), DeviceError::OperatorErr, itr.error_msg().c_str());
        return _logRetValue(true);
    }
    /// 更新数据
    bool Update(IRowBy& by, const ByteArray& data)
    {
        LOG_FUNC_NAME();
        lmdb_cursor_const_iterator ritr = _dbi.cursor();
        ASSERT_FuncInfoRet(ritr.is_valid(), ritr.error_msg().c_str());
        ASSERT_FuncErrRet(by.Find(ritr), DeviceError::ArgRangeErr);
        LOGGER(_log << "Key:<" << _hex(ritr.Key) << ">\n");
        LOGGER(_log << "Value:<" << _hex(data) << ">\n");
        lmdb_cursor_iterator itr = _dbi.cursor();
        bool bOK = itr.put(ritr.Key, data);
        ASSERT_FuncInfoRet(bOK, itr.error_msg().c_str());
        itr.revert();
        return _logRetValue(true);
    }
    /// 更新行
    bool Update(const DataRow& row)
    {
        if(row.IsNull())
        {
            _logErr(DeviceError::ArgIsNullErr);
            return false;
        }
        string val;
        row.Message().Encode(val);
        return Update(ByKey(row.Key()), ByteArray(val.c_str(), val.length()));
    }
    /// 提交更改
    bool Commit()
    {
        LOG_FUNC_NAME();
        lmdb_cursor_iterator itr = _dbi.cursor();
        bool bOK = itr.commit();
        if(!itr.revert())
        {
            _logErr(DeviceError::OperatorStatusErr, itr.error_msg().c_str());
        }
        return _logRetValue(bOK);
    }
};
//--------------------------------------------------------- 
/**
 * @brief 数据集,维护多张表
 * @date 2018-06-26 16:18
 */
class DataSet : public DeviceBehavior
{
public:
    typedef std::pair<string, DataTable> PairType;
    typedef std::map<string, DataTable> MapType;
    typedef MapType::iterator iterator;
    typedef MapType::const_iterator const_iterator;
protected:
    lmdb_env _env;
    MessageLoader _loader;
public:
    MapType Tables;
    /**
     * @brief 从指定的数据库中加载
     * @date 2018-06-26 09:10
     */
    size_t Load(const string& db)
    {
        LOG_FUNC_NAME();
        LOGGER(_log << "Dir:<" << db << ">\n");
        size_t count = 0;
        lmdb_const_env renv = lmdb_const_env::create(db.c_str(), 0, lmdb_env::default_dbs);
        if(!renv.is_valid())
        {
            _logErr(DeviceError::DevInitErr, _env.error_msg().c_str());
            return count;
        }
        lmdb_const_dbi rdbi = renv.begin().open("DataTable");
        lmdb_cursor_const_iterator ritr = rdbi.cursor();
        if(!ritr.is_valid())
        {
            _logErr(DeviceError::DevOpenErr, ritr.error_msg().c_str());
            return count;
        }
        // 在db目录下生成临时的proto文件
        string protodir = db;
        protodir += "_proto";
        FolderHelper::CreateFolder(protodir.c_str());
        protodir += PATH_SEPARATOR;

        list_t<string> tableNames;
        LmdbStringEnumerator enumer = ritr;
        while(enumer.MoveNext())
        {
            string path = protodir;
            path += enumer.Current().Key;
            path += ".proto";
            LOGGER(_log << "Path:<" << path << ">\n");

            string& val = enumer.Current().Value;
            ByteArray buff(val.c_str(), val.length());
            if(!ProtoFileHelper::Decode(buff, path.c_str()))
            {
                LOGGER(_log.WriteLine("Decode Error"));
                continue;
            }
            if(!_loader.ImportFile(path))
            {
                LOGGER(_log.WriteLine("ImportFile Error"));
                continue;
            }
            tableNames.push_back(enumer.Current().Key);
        }
        _env = lmdb_env::create(renv);
        if(!_env.is_valid())
        {
            _logErr(DeviceError::DevOpenErr, _env.error_msg().c_str());
            return count;
        }
        for(auto name : tableNames)
        {
            lmdb_dbi dbi = _env.begin().open(name.c_str());
            if(!dbi.is_valid())
            {
                LOGGER(_log << "Name:<" << name << ">:" << dbi.error_msg());
                continue;
            }

            std::pair<iterator, bool> insertItr;
            insertItr = Tables.insert(PairType(name, DataTable()));
            DataTable& dataTable = insertItr.first->second;
            LOGGER(dataTable.SelectLogger(_log));
            if(dataTable.Load(dbi, _loader))
            {
                ++count;
            }
        }
        return count;
    }
};
//--------------------------------------------------------- 
} // namespace datatable
} // namespace wrapper
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_DATATABLE_H_
//========================================================= 